/**
 * a component that hooks the now playing extension into Telescope
 */

// Constants for convience
if (typeof(Cc) == "undefined")
  var Cc = Components.classes;
if (typeof(Ci) == "undefined")
  var Ci = Components.interfaces;
if (typeof(Cu) == "undefined")
  var Cu = Components.utils;
if (typeof(Cr) == "undefined")
  var Cr = Components.results;

Cu.import("resource://gre/modules/XPCOMUtils.jsm");

/**
 * telescope
 */
function nowplayingHooks() {
			Cu.import("resource://telescope/modules/HTTPUtils.js");
			Cu.import("resource://telescope/modules/Preferences.js"); 

		   Cu.import("resource://app/jsmodules/sbProperties.jsm");
		   Cu.import("resource://app/jsmodules/sbLibraryUtils.jsm");
};

nowplayingHooks.prototype = {  
  /**
   * XPCOM Setup
   */
  classDescription: "Hooks Now Playing commands into Telescope",
  classID:          Components.ID("{2459e9f0-e988-11de-8a39-0800200c9a66}"),  // unique CID
  contractID:       "@awilco.net/telescope/nowplayinghooks;1",             // "@mozilla.org/example;1

  /**
   * The xpcom factory for this component, if not included then the default
   * will be used:
   *  new className()).QueryInterface(iid)
  //_xpcom_factory : { },
 
  /**
   * The Catagorys for this component, this can be used to search for all
   * components in the same catagory. It also defines if this is an instance
   * or a service. These are optional so you can comment out if you don't need
   * it, this will default to an instance in that case.
   */
  _xpcom_categories: [{
    category: "app-startup",  // Catagory for this component
    entry: "nowplayinghooks",        // Catagory entry name
    service: true                    // Set to true to make this a service
  }],
 
  // Class functions and members here
 
  // Class functions and members here
  _nps: null,
  _directory: '/nowplaying/',
  //place startup code here
  observe: function(aSubject, aTopic, aData) 
  {
		switch(aTopic) 
		{
		  case "app-startup":
            var obsService = Cc["@mozilla.org/observer-service;1"]
                           .getService(Ci.nsIObserverService);
            obsService.addObserver(this,'profile-after-change',false);
            break;
          case "profile-after-change":
		  
			//attempt to get the now playing service
			try
			{
				this._nps = Cc["@songbirdnest.com/Songbird/now-playing/service;1"]
										.getService(Ci.sbINowPlayingService);
			   
			    if (this._nps == null)
				{
				
					dump('Could not find NPS service!\n');
					return;
				}
					
				var telescope = Components.classes["@awilco.net/telescope;1"].getService(Components.interfaces.nsISupports);
				telescope = telescope.wrappedJSObject;
			
				telescope.server.registerPathHandler(this._directory,this);
							
				
				telescope.addHook('mediaItem','NP_QueueNext',this._directory + 'commands?action=queueNext&guid=<< #guid >>','Queue Next');
				telescope.addHook('mediaItem','NP_QueueLast',this._directory + 'commands?action=queueLast&guid=<< #guid >>','Queue Last');
		    }
			catch (e)
			{
				return;
			}
			break;
	    }
  },		
 
  /**
   * Handles internal service requests, use path as opposed to metadata.path
   * path is an array of path components
   */
  handle: function(metadata,response)
  {
		path = metadata.path.substring(this._directory.length).split('/');
		
		var ext = this.getExtension(metadata.path);
		if (ext == '') ext = 'json';
		
		var params = HTTPUtils.decodeURIParameters(metadata.queryString);
		
		var comp = path.shift().toLowerCase();
		var data = null;
		switch(comp)
		{
			case 'commands':
				this.processCommands(params,metadata,response);
				break;
			default:
				throw HTTPUtils.HTTP_404;
		}
		
		//write data
		if (data)
        {
            response.setHeader("Content-Type", 'application/javascript', false);
			response.write(remoted.JSON.stringify(data));
        }
	},
   
    processCommands: function(params,metadata,response)
	{
		if (this._nps == null)
			throw HTTPUtils.HTTP_404;
		
		if (!params.action)
			throw HTTPUtils.HTTP_400;
			
		switch (params.action)
		{
			case 'queueNext':
				var guid = params.guid;
				if (!guid) throw HTTPUtils.HTTP_400;
				var item = LibraryUtils.mainLibrary.getItemByGuid(guid);
				if (!item) throw HTTPUtils.HTTP_404;
				
				//Following code from NowPlayingCommands.js, thanks j.c.m
				var mm  = Cc["@songbirdnest.com/Songbird/Mediacore/Manager;1"]
								.getService(Ci.sbIMediacoreManager);
								
				
				try {
				  if (mm.sequencer.sequencePosition + 1 < this._nps.currentQueue.view.length) {
					this._nps.currentQueue.insertSomeAt(new ArrayEnumerator([item]), mm.sequencer.sequencePosition + 1)
				  }
				  else {
					// Sequence position is last, can't insert after the last position, have to append
					this._nps.currentQueue.addSome(new ArrayEnumerator([item]));
				  }
				}
				catch (e) {
				  // Sequence position is not available
				  dump('error: ' + e + '\n');
				  this._nps.currentQueue.addSome(new ArrayEnumerator([item]));
				}
				response.setStatusLine("1.1",303,"See Other");
				response.setHeader('Location','/#nowplaying',false);
				break;
			case 'queueLast':
				var guid = params.guid;
				if (!guid) throw HTTPUtils.HTTP_400;
				var item = LibraryUtils.mainLibrary.getItemByGuid(guid);
				if (!item) throw HTTPUtils.HTTP_404;
				
				//Following code from NowPlayingCommands.js, thanks j.c.m
				var mm  = Cc["@songbirdnest.com/Songbird/Mediacore/Manager;1"]
								.getService(Ci.sbIMediacoreManager);
								
				
				
				this._nps.currentQueue.addSome(new ArrayEnumerator([item]));
				response.setStatusLine("1.1",303,"See Other");
				response.setHeader('Location','/#nowplaying',false);
				break;
		}
	},

	/**
   * extracts the file extension from path info
   */
	getExtension: function(path)
	{
		var startIndex = path.indexOf('.');
		if (startIndex == -1)
			return '';
		
		var endIndex = path.indexOf('?');
		if (endIndex == -1)
			return path.substring(startIndex+1).toLower();
		else
			return path.substring(startIndex+1,endIndex-startIndex-1).toLower();
	},
	
  /**
   * Generates a query interface, include all components this class will
   * implement except nsISupports which is included by default.
   */
  QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.awIHttpRequestHandler, Ci.nsISupportsWeakReference])
};

/**
 * NSGetModule is called to generate the module, pass all classes that need to
 * be implemented.
 */
function NSGetModule(compMgr, fileSpec) {
  return XPCOMUtils.generateModule([nowplayingHooks]);
}

// Copyright 2005 and onwards, Google


/**
 * A simple enumerator around a JavaScript array. Satisfies nsISimpleEnumerator
 */
function ArrayEnumerator(aItems) {
  this._index = 0;
  this._contents = aItems;
}

ArrayEnumerator.prototype = {

  QueryInterface: function(iid) {
    if (iid.equals(Ci.nsISupports) ||
        iid.equals(Ci.nsISimpleEnumerator)) {
      return this;
    } else {
      throw Components.results.NS_ERROR_NO_INTERFACE;
    }
  },

  hasMoreElements: function() {
    return this._index < this._contents.length;
  },

  getNext: function() {
    return this._contents[this._index++];
  },
  get numItems()
  {
	return this._contents.length;
  }
};
